import {
  Component,
  OnInit,
  ViewEncapsulation,
  ChangeDetectionStrategy,
  Input,
  ElementRef,
  Renderer2,
  ContentChild,
  TemplateRef,
  ViewChild,
  ChangeDetectorRef,
  OnChanges,
  SimpleChanges,
  Output,
  EventEmitter,
  KeyValueChanges,
  KeyValueDiffer,
  KeyValueDiffers,
  DoCheck,
  IterableDiffer,
  IterableDiffers,
  IterableChanges,
} from '@angular/core';
import { MenuItem, I18NService, LvConfig, InputBoolean } from '../core/api';

@Component({
  selector: 'lv-operation-menu',
  templateUrl: './operation-menu.component.html',
  exportAs: 'lvOperationMenu',
  styles: [],
  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush,
  host: {
    '[class.lv-operation-menu]': 'true',
  },
})
export class OperationMenuComponent implements OnInit, OnChanges, DoCheck {
  // 行数据。
  @Input('lvData') data: any;
  // 操作项之间的间距
  @Input('lvSpace') space: string = '24px';
  @Input() @InputBoolean() lvDisabled = false;
  // 转化为items数据。类型：(data: any) => Array<MenuItem>
  @Input('lvItemsFn') itemsFn: (data: any) => Array<MenuItem>;
  @Output() lvItemClick: EventEmitter<any> = new EventEmitter();
  @Output() lvOpenChange: EventEmitter<any> = new EventEmitter();
  // 点击overlay外部时是否关闭
  @Input() lvOuterClosable: boolean | { includes?: string[] } = true;
  // item模板
  @ContentChild(TemplateRef, { static: false }) itemTemplate: TemplateRef<any>;

  /**
   * 下拉菜单target元素显示文本
   */
  @Input('lvMenuText') menuText: string = LvConfig.operationMenuOptions.lvMenuText;

  // 最大展示个数（包含下拉菜单）
  @Input('lvMaxShowNum') maxShowNum: number = LvConfig.operationMenuOptions.lvMaxShowNum || 3;

  @ViewChild('dropdownElement', { read: ElementRef, static: false })
  dropdownElementRef: ElementRef;

  // 显示模式
  @Input('lvShowMode') showMode: 'default' | 'dropdown' = LvConfig.operationMenuOptions.lvShowMode || 'default';

  // 操作项集合
  public items: Array<MenuItem> = [];
  // 平铺按钮绑定的选项数据
  public menuItems: Array<MenuItem> = [];
  // 是否显示下拉菜单
  public showDropdownMenu: boolean = true;
  // 实际显示的操作项数据
  public renderItems: Array<MenuItem> = [];
  // 是否只显示下拉菜单
  public onlyShowDropdownMenu: boolean = false;
  // 下拉菜单绑定的选项数据
  public dropdownMenusItems: Array<MenuItem> = [];

  private dataDiffer: KeyValueDiffer<any, any>;

  private itemsDiffer: IterableDiffer<MenuItem>;

  constructor(
    public elementRef: ElementRef,
    protected renderer: Renderer2,
    protected cdr: ChangeDetectorRef,
    private keyValueDiffers: KeyValueDiffers,
    private i18n: I18NService,
    private iterableDiffers: IterableDiffers,
  ) {}

  /**
   * ngFor遍历的 trackBy函数，防止数据更新导致所有DOM重新渲染
   */
  trackByIndex(index, item): any {
    return index;
  }

  showDropdown(): void {
    this.lvOpenChange.emit({ isOpen: true, data: this.data });
  }

  hideDropdown(): void {
    this.lvOpenChange.emit({ isOpen: false, data: this.data });
  }

  /**
   * 判断是否不为空
   */
  isNotNull(value: any): boolean {
    return typeof value !== 'undefined' && value !== null;
  }

  ngOnInit(): void {
    // 判断是不是只显示下拉菜单
    if (this.maxShowNum === 1) {
      this.onlyShowDropdownMenu = true;
    }

    if (this.itemsFn) {
      this.items = this.itemsFn(this.data);
    }

    this.initItems();
  }

  ngOnChanges(changes: SimpleChanges): void {
    if (changes.maxShowNum) {
      if (this.maxShowNum <= 1) {
        this.maxShowNum = 1;
      } else {
        this.maxShowNum = changes.maxShowNum.currentValue;
      }

      this.onlyShowDropdownMenu = this.maxShowNum === 1 ? true : false;
      if (!changes.maxShowNum.firstChange) {
        this.initItems();
      }
      this.cdr.markForCheck();
    }
  }

  ngDoCheck(): void {
    // 当数据发生变化时，重新执行itemsFn，刷新的items内容
    if (this.data) {
      if (!this.dataDiffer) {
        this.dataDiffer = this.keyValueDiffers.find(this.data).create();
      }
      const dataChanges: KeyValueChanges<any, any> | null = this.dataDiffer.diff(this.data);
      if (dataChanges) {
        this.items = this.itemsFn(this.data);
      }
    }

    if (this.items) {
      if (!this.itemsDiffer) {
        this.itemsDiffer = this.iterableDiffers.find(this.items).create();
      }
      const itemChanges: IterableChanges<MenuItem> | null = this.itemsDiffer.diff(this.items);
      if (itemChanges) {
        this.initItems();
      }
    }
  }

  /**
   * 点击数据项
   */
  onClick(event, item?): void {
    // 下拉菜单的返回值event,instance,item，需要判断是下拉菜单还是button
    const menuItem = event.item ? event.item : item;
    const e = event.event ? event.event : event;
    if (!menuItem.disabled) {
      if (menuItem.onClick) {
        if (!menuItem._onClickEmitter) {
          menuItem._onClickEmitter = new EventEmitter();
          menuItem._onClickEmitter.subscribe(menuItem.onClick);
        }

        menuItem._onClickEmitter.emit({
          event: e,
          item: menuItem,
        });
      } else {
        this.lvItemClick.emit({
          event: e,
          item: menuItem,
        });
      }
    } else {
      event.preventDefault();
      return;
    }
  }

  /**
   * 判断是否显示item
   * 判断哪些item应该平铺展示，哪些应该放入下拉菜单
   * 调整平铺展示的item，调整下拉菜单中展示的item，调整menuText的文字
   */
  initItems(): void {
    /**
     * 判断是否需要显示下拉菜单：
     * 1、无数据和无显示数据，不需要显示下拉菜单
     * 2、items个数没有超过最大显示个数，不需要显示下拉菜单
     * 3、items个数超过最大显示个数，需要显示下拉菜单
     */
    if (!this.isNotNull(this.items)) {
      this.showDropdownMenu = false;
      this.cdr.markForCheck();
      return;
    }

    this.renderItems = this.items.filter((item): boolean => !item.hidden);

    if (!this.renderItems.length) {
      this.showDropdownMenu = false;
      this.cdr.markForCheck();
      return;
    }

    if (this.showMode === 'default') {
      if (this.renderItems.length <= this.maxShowNum) {
        this.showDropdownMenu = false;
        this.onlyShowDropdownMenu = false;
        this.menuItems = this.renderItems;
      } else if (this.renderItems.length > this.maxShowNum) {
        this.menuItems = this.renderItems.slice(0, this.maxShowNum - 1);
        this.dropdownMenusItems = this.renderItems.slice(this.maxShowNum - 1);
        this.showDropdownMenu = true;
      }
    } else {
      this.showDropdownMenu = true;
      this.onlyShowDropdownMenu = true;
      this.dropdownMenusItems = this.renderItems;
    }

    this.cdr.markForCheck();
  }
}
